package pingce.lucene.sentiment;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

import pingce.conf.GlobalProperties;
import pingce.share.model.PairValue;
import pingce.share.model.SentenceResult;
import pingce.share.model.SplitResult;
import pingce.share.model.SubSentenceResult;
import pingce.share.model.TestDocumentResult;
import pingce.share.model.WeiboResult;
import pingce.share.model.raw.Sentence;
import pingce.share.model.raw.TestDocument;
import pingce.share.model.raw.Weibo;
import pingce.share.util.Splitter;
import pingce.share.util.SplitterFactory;
import pingce.share.util.WrappedSplitter;


public class SearchSentimentWords {
	private String indexDir;
	private IndexSearcher searcher;
	private static final String DEFAULT_LENGTH_ONE_FILE = "sentiment-dictionary/情感词单字.csv";
	private HashMap<String, String> lengthOneWordPOSMap; 
	
	private static String[] allowedPOS = {
		"/a", "/an", "/v", "/n","/vn","/l"
	}; 
	private static Set<String> allowedPOSSet;
	static{
		allowedPOSSet = new HashSet<String>();
		for(String pos : allowedPOS)
			allowedPOSSet.add(pos);
	}
	
	public SearchSentimentWords(String indexDir){
		this(indexDir, DEFAULT_LENGTH_ONE_FILE);
	}
	
	public SearchSentimentWords(String indexDir, String singleWordFile){
		this.indexDir = indexDir;
		try{
			Directory dir= FSDirectory.open(new File(indexDir));
			IndexReader reader= IndexReader.open(dir);
			searcher = new IndexSearcher(reader);
			
//			initialize lengthOneWordPOSMap
			this.lengthOneWordPOSMap = new HashMap<String, String>();
			BufferedReader br = new BufferedReader(
					new InputStreamReader(new FileInputStream(singleWordFile), "UTF-8"));
			String temp;
			while((temp = br.readLine()) != null){
				String[] arr = temp.split(",");
				if(arr.length > 2){//this word need to be refined by pos tag
					String word = arr[0];
					String POSes = "";
					for(int i = 2; i < arr.length; i++){
						POSes += arr[i].replace("/", "") + " ";
					}
					this.lengthOneWordPOSMap.put(word, POSes.trim());
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public String getIndexDir() {
		return indexDir;
	}
	public void setIndexDir(String indexDir) {
		this.indexDir = indexDir;
	}
	public IndexSearcher getSearcher() {
		return searcher;
	}
	public void setSearcher(IndexSearcher searcher) {
		this.searcher = searcher;
	}
	public List<SentimentWord> search(SentenceResult sr){
		List<SentimentWord> sentimentList = new LinkedList<SentimentWord>();
		for(SubSentenceResult ssr : sr.getSubSentences()){
			if(ssr.getType() == WrappedSplitter.NORMAL &&  ssr.getRawText().length() > 0){//only extract sentiments in normal text
				String rawText = ssr.getRawText();
				AllDocCollector collector = new AllDocCollector();
				String spacedRawText = Utility.toSpaceString(rawText);
				String[] charArray = spacedRawText.split(" ");
				try{
					BooleanQuery bQuery = new BooleanQuery();
					for(String _char : charArray){
						TermQuery tQuery = new TermQuery(new Term("word", _char));
						bQuery.add(tQuery, BooleanClause.Occur.SHOULD);
					}
					searcher.search(bQuery, collector);
					List<ScoreDoc> list = collector.getHist();
					for(ScoreDoc sd : list){
						Document doc = searcher.doc(sd.doc);
						String word = doc.getField("word").stringValue().replaceAll(" ", "");
						String polarity = doc.getField("polarity").stringValue();
						if (rawText.indexOf(word) != -1) {
							List<SentimentWord> wordList = constructSentimentWord(
									ssr, word, polarity);
							sentimentList.addAll(wordList);
						}
					}
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}
		return filterLenthOne(
				filterWrongPosWords(
				filterOverlapWords(
						sentimentList)));
	}
	private List<SentimentWord> constructSentimentWord(SubSentenceResult ssr, String word, String polarity){
		Pattern p = Pattern.compile(word);
		Matcher m = p.matcher(ssr.getRawText());
		List<SentimentWord> list = new LinkedList<SentimentWord>();
		SplitResult splitResult = ssr.getSplitResult();
		while(m.find()){
			String text = m.group();
			int match = matchSplitResult(splitResult, m);
			if (match == 0) {// the sentiment is a single word
				int start = m.start() + ssr.getBegin();
				int end = m.end() + ssr.getBegin() - 1;
				PairValue pv = splitResult.getResultMap()
						.get(start + ":" + end);
				if (pv == null) {
					System.err.println("error while find POS tag for word="
							+ text + " at start =" + start + " end=" + end);
				}else{
					String pos = pv.getAttach();
					SentimentWord sw = new SentimentWord(text, polarity, start, end, word, true, pos);
					list.add(sw);
				}
			}else if(match == 1){
				int start = m.start() + ssr.getBegin();
				int end = m.end() + ssr.getBegin() - 1;
				SentimentWord sw = new SentimentWord(text, polarity, start, end, word);
				list.add(sw);
			}
		}
		return list;
	}
	
	private List<SentimentWord> filterLenthOne(List<SentimentWord> sentimentWords){
		List<SentimentWord> resultList = new LinkedList<SentimentWord>();
		for(SentimentWord sw : sentimentWords){
			if (this.lengthOneWordPOSMap.containsKey(sw.getText())){
				String POSes = this.lengthOneWordPOSMap.get(sw.getText());
				String[] posArr = POSes.split(" ");
				for(String pos : posArr){
					if(sw.getPOS().startsWith("/" + pos)){
						resultList.add(sw);
						break;
					}
				}
			}else{
				resultList.add(sw);
			}
		}
		return resultList;
	}
	
	private static List<SentimentWord> filterWrongPosWords(List<SentimentWord> sentimentWords){
		List<SentimentWord> resultList = new LinkedList<SentimentWord>();
		for(SentimentWord sw : sentimentWords){
			if(sw.isSingleWord()){
				if(allowedPOSSet.contains(sw.getPOS())){
					resultList.add(sw);
				}
			}else
				resultList.add(sw);
		}
		return resultList;
	}
	
	private static List<SentimentWord> filterOverlapWords(List<SentimentWord> sentimentWords){
		List<SentimentWord> resultList = new LinkedList<SentimentWord>();
//		sort by start
		PriorityQueue<SentimentWord> pq = new PriorityQueue<SentimentWord>();
		for(SentimentWord sw : sentimentWords){
			pq.add(sw);
		}
//		Of all the overlapped words, pick out the longest one
		while(!pq.isEmpty()){
			SentimentWord sw = pq.poll();
			SentimentWord maxWord = sw;
			while(!pq.isEmpty()){
				SentimentWord sw2 = pq.peek();
				if(overlap(sw.getStart(), sw.getEnd(), sw2.getStart(), sw2.getEnd())){
					sw2 = pq.poll();
					if(sw2.getText().length() > sw.getText().length())
						maxWord = sw2;
				}else{
					break;
				}
			}
			resultList.add(maxWord);
		}
		
		return resultList;
	}
	
	private static boolean overlap(int start1, int end1, int start2, int end2){
		if((start1 >= start2 && start1 <= end2) ||
				(end1 >= start2 && end1 <= end2))
			return true;
		else
			return false;
	}
	
	private static int matchSplitResult(SplitResult sr, Matcher m){
		int mStart = m.start();
		int mEnd = m.end();
		int currentPos = 0;
		String[] arr = sr.getNoPosArrayResult();
		boolean startEqual = false, endEqual = false;
		boolean singleToken = false;
		for(String s : arr){
			if(s.length() > 0){
				int sEnd = currentPos + s.length();
				if(currentPos == mStart){
					startEqual = true;
				}
				if(sEnd == mEnd){
					endEqual = true;
				}
				if(currentPos == mStart && sEnd == mEnd){
					singleToken = true;
				}
				currentPos = sEnd;
			}
		}
		
		if(startEqual && endEqual)
			if(singleToken)
				return 0;
			else
				return 1;
		else
			return -1;
	}
	
	public static void main(String[] args){
		String path = "lucene/polarity";
//		String sentence = "大不了同归于尽还为社会除害";
//		String sentence = "他是一个很残忍的人";
		long start = System.currentTimeMillis();
		SearchSentimentWords std = new SearchSentimentWords(path);
		
		GlobalProperties gp = GlobalProperties.getInstance();
//		String topicTitle = "生产实习演示";
		String topicTitle = "jiang_zhuang_zhi_ru_guang_gao";
		TestDocumentResult tdr = TestDocumentResult.readForm(gp.get(topicTitle, "parseResult"));
		for(WeiboResult wr : tdr.getWeiboResultList()){
			for(SentenceResult sr : wr.getSentences()){
				List<SentimentWord> list = std.search(sr);
//				System.out.println("sentence :" + sr.getRawText());
				for(SentimentWord sw : list){
					System.out.println(sw);
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("Time consumpution:" + (end - start)/1000);
	}
}
